;void prewitt(char* matriz, unsigned alto, unsigned ancho, char* matriz2)
global prewitt

section.text

%define matriz  [ebp + 8] 
%define alto    [ebp + 12]
%define ancho   [ebp + 16]
%define matriz2 [ebp + 20]


; -----------------------------------------------------------------------------
; Prewitt: Aplica el operador de detección de bordes de Prewitt
; -----------------------------------------------------------------------------

prewitt:
    push    ebp
    mov     ebp, esp
    push    esi
    push    edi
    push    ebx

init:
    mov     esi, matriz
    mov     edi, matriz2
    mov     ebx, ancho
    mov     ecx, alto

    ; si la matriz tiene menos de 3x3 en alguna dimension, no se puede
    ; ejecutar el operador de prewitt en ningun pixel: copio la matriz original
    ; y devuelvo
    cmp     ebx, 3
    jb      copiarYDevolver
    cmp     ecx, 3
    jb      copiarYDevolver


copiarPrimeraFila:
    ; copio la primera fila de la matriz
    push    ebx
    push    edi
    push    esi
    call    copiarBytes
    add     esp, 12

    add     esi, ebx
    add     edi, ebx

    ; resto 2 a la cantidad de filas
    ; ya que la primera y ultima fila se copian
    ; a mano, sin usar el bucle
    sub     ecx, 2

loopFilas:
    cmp     ecx, 0
    je      copiarUltimaFila

    mov     edx, ebx
    sub     edx, 2

    ; copio el primer pixel
    mov byte [edi], 0
    add     edi, 1
    add     esi, 1



; -----------------------------------------------------------------------------
; Este bloque aplica el operador de Prewitt con MMX
; -----------------------------------------------------------------------------
; Nota: Se aplica sobre 8 pixeles por vez, aprovechando que se tienen muchos
;       registros disponibles (desenrollado de bucles).

loopColumnas:
    cmp     edx, 8
    jb      completarLoopColumnas

    ; -----------------;    
    ; Calculo de |Gx|  ;
    ; -----------------;

    pxor        mm7, mm7
    pxor        mm0, mm0
    pxor        mm2, mm2
    neg         ebx


    ; arriba a la izquierda
    movq        mm1, [esi + ebx - 1]
    movq        mm3, mm1
    punpcklbw   mm1, mm7
    punpckhbw   mm3, mm7
    psubw       mm0, mm1
    psubw       mm2, mm3

    ; arriba a la derecha
    movq        mm1, [esi + ebx + 1]
    movq        mm3, mm1
    punpcklbw   mm1, mm7
    punpckhbw   mm3, mm7
    paddw       mm0, mm1
    paddw       mm2, mm3

    ; centro izquierda
    movq        mm1, [esi - 1]
    movq        mm3, mm1
    punpcklbw   mm1, mm7
    punpckhbw   mm3, mm7
    psubw       mm0, mm1
    psubw       mm2, mm3

    ; centro derecha
    movq        mm1, [esi + 1]
    movq        mm3, mm1
    punpcklbw   mm1, mm7
    punpckhbw   mm3, mm7
    paddw       mm0, mm1
    paddw       mm2, mm3

    neg ebx

    ; abajo a la izquierda
    movq        mm1, [esi + ebx - 1]
    movq        mm3, mm1
    punpcklbw   mm1, mm7
    punpckhbw   mm3, mm7
    psubw       mm0, mm1
    psubw       mm2, mm3

    ; abajo a la derecha
    movq        mm1, [esi + ebx + 1]
    movq        mm3, mm1
    punpcklbw   mm1, mm7
    punpckhbw   mm3, mm7
    paddw       mm0, mm1
    paddw       mm2, mm3


    ; tomo modulo de las sumas en 16 bits
    pxor        mm4, mm4
    pcmpgtw     mm4, mm0
    pand        mm4, mm0
    psubw       mm0, mm4
    psubw       mm0, mm4

    pxor        mm4, mm4
    pcmpgtw     mm4, mm2
    pand        mm4, mm2
    psubw       mm2, mm4
    psubw       mm2, mm4


    ; vuelvo a packear en 8 bits con saturacion
    ; y guardo el resultado en mm6
    packuswb    mm0, mm2
    movq        mm6, mm0
   
    ; -----------------;    
    ; Calculo de |Gy|  ;
    ; -----------------;
    
    neg         ebx
    pxor        mm0, mm0
    pxor        mm2, mm2

    ; arriba a la izquierda
    movq        mm1, [esi + ebx - 1]
    movq        mm3, mm1
    punpcklbw   mm1, mm7
    punpckhbw   mm3, mm7
    psubw       mm0, mm1
    psubw       mm2, mm3
    
    ; arriba al medio
    movq        mm1, [esi + ebx]
    movq        mm3, mm1
    punpcklbw   mm1, mm7
    punpckhbw   mm3, mm7
    psubw       mm0, mm1
    psubw       mm2, mm3


    ; arriba a la derecha
    movq        mm1, [esi + ebx + 1]
    movq        mm3, mm1
    punpcklbw   mm1, mm7
    punpckhbw   mm3, mm7
    psubw       mm0, mm1
    psubw       mm2, mm3

    neg         ebx

    ; abajo a la izquierda
    movq        mm1, [esi + ebx + 1]
    movq        mm3, mm1
    punpcklbw   mm1, mm7
    punpckhbw   mm3, mm7
    paddw       mm0, mm1
    paddw       mm2, mm3

    ; abajo al medio
    movq        mm1, [esi + ebx]
    movq        mm3, mm1
    punpcklbw   mm1, mm7
    punpckhbw   mm3, mm7
    paddw       mm0, mm1
    paddw       mm2, mm3


    ; abajo a la derecha
    movq        mm1, [esi + ebx + 1]
    movq        mm3, mm1
    punpcklbw   mm1, mm7
    punpckhbw   mm3, mm7
    paddw       mm0, mm1
    paddw       mm2, mm3


    ; tomo modulo de las sumas en 16 bits
    pxor        mm4, mm4
    pcmpgtw     mm4, mm0
    pand        mm4, mm0
    psubw       mm0, mm4
    psubw       mm0, mm4
    
    pxor        mm4, mm4
    pcmpgtw     mm4, mm2
    pand        mm4, mm2
    psubw       mm2, mm4
    psubw       mm2, mm4


    ; vuelvo a packear en 8 bits con saturacion
    ; y sumo el resultado a |Gx| que estaba en mm6
    packuswb    mm0, mm2
    paddusb     mm6, mm0
    
    ; guardo el resultado, incremento y continuo
    movq    [edi], mm6
    add     esi, 8
    add     edi, 8
    sub     edx, 8
    jmp     loopColumnas


; -----------------------------------------------------------------------------
; Este bloque completa los bytes excendentes del final de cada fila
; -----------------------------------------------------------------------------
; Nota: El codigo basicamente reimplementa Prewitt sin MMX, con lo cual
;       resulta bastante extenso.

completarLoopColumnas:
    ; proceso los bytes restantes de a uno
    cmp     edx, 0
    je      finLoopColumnas

    ; necesito 2 registros extra, los pusheo sin incurrir
    ; en grandes penalidades ya que esto se realiza a lo sumo
    ; 7 veces por fila de la imagen, y el stack esta en cache
    push    edx
    push    ecx

    ; calculo |Gx|
    neg     ebx
    xor     eax, eax
    xor     ecx, ecx
    mov     cl, [esi+ebx-1]
    sub     ax, cx
    mov     cl, [esi+ebx+1]
    add     ax, cx
    neg     ebx
    mov     cl, [esi-1]
    sub     ax, cx
    mov     cl, [esi+1]
    add     ax, cx
    mov     cl, [esi+ebx-1]
    sub     ax, cx
    mov     cl, [esi+ebx+1]
    add     ax, cx

    cmp     ax, 0
    jge     noNegarGx
    neg     ax
noNegarGx:
    mov     ax, dx

    ; calculo |Gy|
    neg     ebx
    xor     eax, eax
    mov     cl, [esi+ebx-1]
    sub     ax, cx    
    mov     cl, [esi+ebx]
    sub     ax, cx
    mov     cl, [esi+ebx+1]
    sub     ax, cx
    neg     ebx
    mov     cl, [esi+ebx-1]
    add     ax, cx
    mov     cl, [esi+ebx]
    add     ax, cx
    mov     cl, [esi+ebx+1]
    add     ax, cx

    cmp     ax, 0
    jge     noNegarGy
    neg     ax
noNegarGy:

    ; calculo |Gx|+|Gy|, saturo y guardo
    add     edx, eax
    cmp     edx, 255
    jbe     noSaturar
    mov     edx, 255
noSaturar:
    mov     [edi], dl

    ; recupero los registros
    pop     ecx
    pop     edx

    add     edi, 1
    add     esi, 1
    sub     edx, 1
    jmp     completarLoopColumnas


; -----------------------------------------------------------------------------
; Bloque que termina cada fila y procede con el algoritmo.
; -----------------------------------------------------------------------------

finLoopColumnas:

    ; copio el ultimo pixel    
    mov byte [edi], 0
    add     edi, 1
    add     esi, 1

    sub     ecx, 1
    jmp     loopFilas

copiarUltimaFila:
    push    ebx
    push    edi
    push    esi
    call    copiarBytes
    add     esp, 12

    add     esi, ebx
    add     edi, ebx
  
fin:
    emms
    pop     ebx
    pop     edi
    pop     esi
    pop     ebp
    ret

 
; -----------------------------------------------------------------------------
; Bloque que copia y devuelve para matrices de menos de 3x3
; -----------------------------------------------------------------------------
copiarYDevolver:
    mov     al, bl
    mul     cl
    xor     ecx, ecx
    mov     cx, ax

    cmp     ecx, 0
    je      fin

cicloCopiarYDevolver:
    mov byte [edi], 0
    add     esi, 1
    add     edi, 1
    loop    cicloCopiarYDevolver

    jmp     fin


; -----------------------------------------------------------------------------
; Auxiliar para copiar una fila de la matriz
; -----------------------------------------------------------------------------
; Nota: La funcion preserva todos los registros para evitar complicaciones al
;       realizar una llamada.

;void copiarBytes(char* source, char* dest, unsigned n)
copiarBytes:
    push    ebp
    mov     ebp, esp
    push    edi
    push    esi
    push    ebx
    push    ecx

    mov     esi, [ebp + 8]
    mov     edi, [ebp + 12]
    mov     ecx, [ebp + 16]

    cmp     ecx, 0
    je     finCopia

cicloCopiarBytes:
    mov byte [edi], 0
    add     esi, 1
    add     edi, 1
    loop    cicloCopiarBytes

finCopia:
    pop     ecx
    pop     ebx
    pop     esi
    pop     edi
    pop     ebp
    ret
